package messages
import (
"fmt"
"time"
"github.com/jcmturner/gofork/encoding/asn1"
"github.com/jcmturner/gokrb5/v8/asn1tools"
"github.com/jcmturner/gokrb5/v8/crypto"
"github.com/jcmturner/gokrb5/v8/iana"
"github.com/jcmturner/gokrb5/v8/iana/asnAppTag"
"github.com/jcmturner/gokrb5/v8/iana/errorcode"
"github.com/jcmturner/gokrb5/v8/iana/keyusage"
"github.com/jcmturner/gokrb5/v8/iana/msgtype"
"github.com/jcmturner/gokrb5/v8/keytab"
"github.com/jcmturner/gokrb5/v8/krberror"
"github.com/jcmturner/gokrb5/v8/types"
)
type marshalAPReq struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
APOptions asn1 .BitString `asn1:"explicit,tag:2"`
Ticket asn1 .RawValue `asn1:"explicit,tag:3"`
EncryptedAuthenticator types .EncryptedData `asn1:"explicit,tag:4"`
}
type APReq struct {
PVNO int `asn1:"explicit,tag:0"`
MsgType int `asn1:"explicit,tag:1"`
APOptions asn1 .BitString `asn1:"explicit,tag:2"`
Ticket Ticket `asn1:"explicit,tag:3"`
EncryptedAuthenticator types .EncryptedData `asn1:"explicit,tag:4"`
Authenticator types .Authenticator `asn1:"optional"`
}
func NewAPReq (tkt Ticket , sessionKey types .EncryptionKey , auth types .Authenticator ) (APReq , error ) {
var a APReq
ed , err := encryptAuthenticator (auth , sessionKey , tkt )
if err != nil {
return a , krberror .Errorf (err , krberror .KRBMsgError , "error creating Authenticator for AP_REQ" )
}
a = APReq {
PVNO : iana .PVNO ,
MsgType : msgtype .KRB_AP_REQ ,
APOptions : types .NewKrbFlags (),
Ticket : tkt ,
EncryptedAuthenticator : ed ,
}
return a , nil
}
func encryptAuthenticator(a types .Authenticator , sessionKey types .EncryptionKey , tkt Ticket ) (types .EncryptedData , error ) {
var ed types .EncryptedData
m , err := a .Marshal ()
if err != nil {
return ed , krberror .Errorf (err , krberror .EncodingError , "marshaling error of EncryptedData form of Authenticator" )
}
usage := authenticatorKeyUsage (tkt .SName )
ed , err = crypto .GetEncryptedData (m , sessionKey , uint32 (usage ), tkt .EncPart .KVNO )
if err != nil {
return ed , krberror .Errorf (err , krberror .EncryptingError , "error encrypting Authenticator" )
}
return ed , nil
}
func (a *APReq ) DecryptAuthenticator (sessionKey types .EncryptionKey ) error {
usage := authenticatorKeyUsage (a .Ticket .SName )
ab , e := crypto .DecryptEncPart (a .EncryptedAuthenticator , sessionKey , uint32 (usage ))
if e != nil {
return fmt .Errorf ("error decrypting authenticator: %v" , e )
}
err := a .Authenticator .Unmarshal (ab )
if err != nil {
return fmt .Errorf ("error unmarshaling authenticator: %v" , err )
}
return nil
}
func authenticatorKeyUsage(pn types .PrincipalName ) int {
if pn .NameString [0 ] == "krbtgt" {
return keyusage .TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR
}
return keyusage .AP_REQ_AUTHENTICATOR
}
func (a *APReq ) Unmarshal (b []byte ) error {
var m marshalAPReq
_ , err := asn1 .UnmarshalWithParams (b , &m , fmt .Sprintf ("application,explicit,tag:%v" , asnAppTag .APREQ ))
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "unmarshal error of AP_REQ" )
}
if m .MsgType != msgtype .KRB_AP_REQ {
return NewKRBError (types .PrincipalName {}, "" , errorcode .KRB_AP_ERR_MSG_TYPE , errorcode .Lookup (errorcode .KRB_AP_ERR_MSG_TYPE ))
}
a .PVNO = m .PVNO
a .MsgType = m .MsgType
a .APOptions = m .APOptions
a .EncryptedAuthenticator = m .EncryptedAuthenticator
a .Ticket , err = unmarshalTicket (m .Ticket .Bytes )
if err != nil {
return krberror .Errorf (err , krberror .EncodingError , "unmarshaling error of Ticket within AP_REQ" )
}
return nil
}
func (a *APReq ) Marshal () ([]byte , error ) {
m := marshalAPReq {
PVNO : a .PVNO ,
MsgType : a .MsgType ,
APOptions : a .APOptions ,
EncryptedAuthenticator : a .EncryptedAuthenticator ,
}
var b []byte
b , err := a .Ticket .Marshal ()
if err != nil {
return b , err
}
m .Ticket = asn1 .RawValue {
Class : asn1 .ClassContextSpecific ,
IsCompound : true ,
Tag : 3 ,
Bytes : b ,
}
mk , err := asn1 .Marshal (m )
if err != nil {
return mk , krberror .Errorf (err , krberror .EncodingError , "marshaling error of AP_REQ" )
}
mk = asn1tools .AddASNAppTag (mk , asnAppTag .APREQ )
return mk , nil
}
func (a *APReq ) Verify (kt *keytab .Keytab , d time .Duration , cAddr types .HostAddress , snameOverride *types .PrincipalName ) (bool , error ) {
sname := &a .Ticket .SName
if snameOverride != nil {
sname = snameOverride
}
err := a .Ticket .DecryptEncPart (kt , sname )
if err != nil {
return false , krberror .Errorf (err , krberror .DecryptingError , "error decrypting encpart of service ticket provided" )
}
ok , err := a .Ticket .Valid (d )
if err != nil || !ok {
return ok , err
}
if len (a .Ticket .DecryptedEncPart .CAddr ) > 0 {
if !types .HostAddressesContains (a .Ticket .DecryptedEncPart .CAddr , cAddr ) {
return false , NewKRBError (a .Ticket .SName , a .Ticket .Realm , errorcode .KRB_AP_ERR_BADADDR , "client address not within the list contained in the service ticket" )
}
}
err = a .DecryptAuthenticator (a .Ticket .DecryptedEncPart .Key )
if err != nil {
return false , NewKRBError (a .Ticket .SName , a .Ticket .Realm , errorcode .KRB_AP_ERR_BAD_INTEGRITY , "could not decrypt authenticator" )
}
if !a .Authenticator .CName .Equal (a .Ticket .DecryptedEncPart .CName ) {
return false , NewKRBError (a .Ticket .SName , a .Ticket .Realm , errorcode .KRB_AP_ERR_BADMATCH , "CName in Authenticator does not match that in service ticket" )
}
ct := a .Authenticator .CTime .Add (time .Duration (a .Authenticator .Cusec ) * time .Microsecond )
t := time .Now ().UTC ()
if t .Sub (ct ) > d || ct .Sub (t ) > d {
return false , NewKRBError (a .Ticket .SName , a .Ticket .Realm , errorcode .KRB_AP_ERR_SKEW , fmt .Sprintf ("clock skew with client too large. greater than %v seconds" , d ))
}
return true , nil
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .